package com.hyt.it.ogt.ks.api.kw;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.common.apiInfor.DataType;
import com.hyt.common.apiInfor.ParamType;
import com.hyt.it.ogt.ks.api.kw.model.*;
import com.hyt.it.ogt.ks.config.ConfigManager;
import com.hyt.it.ogt.ks.config.KsBizRunTimeException;
import com.hyt.it.ogt.ks.enums.BizStatusCode;
import com.hyt.it.ogt.ks.enums.CandidateStatusCode;
import com.hyt.it.ogt.ks.exception.KsException;
import com.hyt.it.ogt.ks.feign.KwClient;
import com.hyt.it.ogt.ks.feign.model.ExamCandidate;
import com.hyt.it.ogt.ks.feign.model.PaperDataVO;
import com.hyt.it.ogt.ks.feign.model.SubjectiveAnswerVO;
import com.hyt.it.ogt.ks.function.ExecutorFactory;
import com.hyt.it.ogt.ks.function.KsExecutorThreadFactory;
import com.hyt.it.ogt.ks.function.KsQueueHandleExecutor;
import com.hyt.it.ogt.ks.mapper.CandidateProcessMapper;
import com.hyt.it.ogt.ks.mapper.ExamScoreMapper;
import com.hyt.it.ogt.ks.mapper.XyEvalLogMapper;
import com.hyt.it.ogt.ks.model.cjvo.PushCandidateScoreToCjVO;
import com.hyt.it.ogt.ks.model.dto.CandidateAnswerSimpleVO;
import com.hyt.it.ogt.ks.model.entity.*;
import com.hyt.it.ogt.ks.model.vo.*;
import com.hyt.it.ogt.ks.service.*;
import com.hyt.it.ogt.ks.util.Constants;
import com.hyt.it.ogt.ks.util.KeyDefineCommon;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hyt.it.ogt.ks.enums.BizStatusCode.CANDIDATE_APPLY_CHECK_ERROR;

/**
 *
 * 考试对外考务接口
 *
 * @author 刘恒活
 */
@Api(tags = "3.考试对外考务接口", value = "考试对外考务接口")
@ApiSort(3)
@RestController
@RequestMapping("/api/kw")
@Slf4j
public class KwApi {

    @Resource
    private IExamScoreService iExamScoreService;
    @Resource
    private ICandidateLogService iCandidateLogService;
    @Resource
    private ICandidateProcessService iCandidateProcessService;
    @Resource
    private ICandidateAnswerService iCandidateAnswerService;
    @Resource
    private ICandidateAnswerBackupService iCandidateAnswerBackupService;
    @Resource 
    private ExamScoreMapper examScoreMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource 
    private ConfigManager configManager;
    @Resource
    private CandidateProcessMapper candidateProcessMapper;
    @Resource
    private ICandidatePaperService iCandidatePaperService;
    @Resource
    private IThirdInterfaceService iThirdInterfaceService;
    @Resource
    private XyEvalLogMapper xyEvalLogMapper;
    @Resource
    private KwClient kwClient;
    @Resource
    private ICandidateLogBackupService iCandidateLogBackupService;
    @Resource
    private InitCandidateDataService initCandidateDataService;
    @Resource
    private IScoreService iScoreService;
    @Resource
    private IApplyCheckService iApplyCheckService;
    @Resource
    private ICandidatePassService iCandidatePassService;
    @Resource
    private ICandidateLogOtherService iCandidateLogOtherService;
    @Resource
    private ICandidateLogAnswerService iCandidateLogAnswerService;

    @ApiOperationSort(1)
    @ApiOperation(value = "3.1.1 获取考试得分接口", notes = "状态码：18001:成功，42030102：异常", response = CandidateScoreDTO.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	,@ApiImplicitParam(name = "paperId", value = "试卷ID", dataType = DataType.STRING, paramType = ParamType.QUERY, required = false)
    	,@ApiImplicitParam(name = "paperVersion", value = "试卷版本号", dataType = DataType.STRING, paramType = ParamType.QUERY, required = false)
    })
    @GetMapping("getScoreByExamCandidateId")
    public Object getScoreByExamCandidateId(@RequestParam String examCandidateId, @RequestParam(required = false) String paperId,
                                            @RequestParam(required = false) String paperVersion,@RequestParam Integer isBackup){
        try {
            log.info("13.1.1 获取考试得分接口接收的参数【{}】", examCandidateId);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iExamScoreService.getScoreByExamCandidateId(examCandidateId, paperId, paperVersion,isBackup))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.API_KW_GET_SCORE.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.API_KW_GET_SCORE.getCode())
                    .build();
        }
    }

    @ApiOperationSort(2)
    @ApiOperation(value = "3.1.2 获取考生正确率接口", notes = "状态码：18001:成功，42030201：异常", response = CandidateAccuracyDTO.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)})
    @GetMapping("getCandidateAccuracy")
    public Object getCandidateAccuracy(@RequestParam String examCandidateId){
        try {
            log.info("13.1.2 获取考生正确率接口接收的参数【{}】", examCandidateId);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iExamScoreService.getCandidateAccuracy(examCandidateId))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.API_KW_GET_ACCURACY.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.API_KW_GET_ACCURACY.getCode())
                    .build();
        }
    }

    @ApiOperationSort(3)
    @ApiOperation(value = "3.1.3 获取考生事件接口", notes = "状态码：18001:成功，42030301：异常", response = CandidateLogDTO.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "logBackup", value = "考生事件表是否已经备份", dataType = DataType.INT, paramType = ParamType.QUERY, required = true)}
    )
    @GetMapping("getCandidateExamLogDetail")
    public Object getCandidateExamLogDetail(@RequestParam String examCandidateId,@RequestParam Integer logBackup){
        try {
            log.info("3.1.3 获取考生事件接口接收的参数考生id【{}】,考生事件表是否已经备份:【{}】", examCandidateId,logBackup);
            List<CandidateLogDTO> dtos ;
            if(Objects.equals(1,logBackup)){
                dtos = iCandidateLogBackupService.getCandidateExamLogDetail(examCandidateId);
            } else {
                dtos = iCandidateLogService.getCandidateExamLogDetail(examCandidateId);
            }

            // 获取跳过考试步骤操作记录
            List<CandidatePass> candidatePassList = iCandidatePassService.getCandidatePassStepsByCandidateId(examCandidateId);

            if (candidatePassList != null && !CollectionUtils.isEmpty(candidatePassList)) {
                // 组装数据，并且按照操作时间排序
                candidatePassList.stream().forEach(candidatePass -> {
                    CandidateLogDTO candidateLogDTO = new CandidateLogDTO();
                    candidateLogDTO.setLogTypeName(candidatePass.getPassSteps());
                    candidateLogDTO.setOperationTime(candidatePass.getOptionDate());
                    dtos.add(candidateLogDTO);
                });
            }

            //按照操作时间排序
            List<CandidateLogDTO> candidateLogDTOList = new ArrayList<>() ;
            candidateLogDTOList = dtos.stream().sorted(Comparator.comparing(CandidateLogDTO::getOperationTime)).collect(Collectors.toList());

            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(candidateLogDTOList)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.API_KW_GET_EXAM_LOG.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.API_KW_GET_EXAM_LOG.getCode())
                    .build();
        }
    }

    @ApiOperationSort(4)
    @ApiOperation(value = "3.1.4 强制结束考试", notes = "状态码：18001:成功，18315：异常", response = ApiResponse.class)
    @PutMapping("forceFinishExam")
    public Object forceFinishExam(@RequestBody String json){
        try {
            log.info("13.1.3 修改考生信息参数【{}】", json);
            iCandidateProcessService.forceFinishExam(json);
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.UPDATE_CANDIDATE_FAIL.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.UPDATE_CANDIDATE_FAIL.getCode())
                    .build();
        }
    }

    @ApiOperationSort(5)
    @ApiOperation(value = "3.1.5 获取主观题答案", notes = "状态码：18001:成功，42030101：异常", response = SubjectiveAnswerVO.class)
    @GetMapping("getSubjectiveAnswerByExam")
    public Object getSubjectiveAnswerByExam(@RequestParam String examId, @RequestParam(required=false)String admissionNumber,
                                            @RequestParam Integer isBackup) {
        try {
            log.info("13.1.5 获取主观题答案【{}】", examId, admissionNumber);
            List<SubjectiveAnswerVO> subjectiveAnswerList = iCandidateAnswerService.getSubjectiveAnswerByExam(examId, admissionNumber,isBackup);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(subjectiveAnswerList)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_SUBJECTIVE_ANSWER_FAIL.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_SUBJECTIVE_ANSWER_FAIL.getCode())
                    .build();
        }
    }

    @ApiOperationSort(6)
    @ApiOperation(value = "3.1.6 获取考生答案", notes = "状态码：18001:成功，42030601：异常", response = CandidateAnswer.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "isObjective", value = "是否主客观，true客观，null表示查全部", dataType = DataType.STRING,
                    paramType = ParamType.QUERY)})
    @GetMapping("getCandidateAnswer")
    public Object getCandidateAnswer(@RequestParam String examId, @RequestParam String examCandidateId,
                                     @RequestParam(required = false) Boolean isObjective,@RequestParam Integer isBackup) {
        try {        	
        	LambdaQueryWrapper<CandidateProcess> queryCandidateProcess = Wrappers.lambdaQuery();
        	queryCandidateProcess.eq(CandidateProcess::getExamId, examId).eq(CandidateProcess::getCandidateId, examCandidateId);
        	CandidateProcess candidateProcess = iCandidateProcessService.getOne(queryCandidateProcess);
        	Integer maxScoreVer = candidateProcess.getMaxScoreVer();
            LambdaQueryWrapper<CandidateAnswer> query = Wrappers.lambdaQuery();
            query.eq(CandidateAnswer::getExamId, examId)
            .eq(CandidateAnswer::getCandidateId, examCandidateId)
            .eq(CandidateAnswer::getAnswerVer, maxScoreVer);
            // 不满意重做，是以最高分版本为准，而这个版本可能is_del_flag是true
            if(maxScoreVer == 0) {
                query.eq(CandidateAnswer::getDelFlag, false);
            }

            if(isObjective != null){
                query.eq(CandidateAnswer::getObjectiveOrSubjective, isObjective);
            }
            List<CandidateAnswer> list;
            if(Objects.equals(0,isBackup)){
                list = iCandidateAnswerService.list(query);
            } else {
                list = iCandidateAnswerBackupService.selectList(examId,examCandidateId,maxScoreVer,isObjective);
            }
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(list)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_ANSWER_FAIL.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_ANSWER_FAIL.getCode())
                    .build();
        }
    }
    
    @ApiOperationSort(7)
    @ApiOperation(value = "3.1.7 获取考生客观题总分", notes = "状态码：18001:成功，42030701：异常", response = CandidateAnswer.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)})
    @GetMapping("getCandidateObjectiveTotalScore")
    public Object getCandidateObjectiveTotalScore( @RequestParam String examCandidateId) {
        try {           
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(examScoreMapper.getCandidateObjectScore(examCandidateId))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_OBJECT_SCORE_CODE.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_OBJECT_SCORE_CODE.getCode())
                    .build();
        }
    }

    @ApiOperationSort(8)
    @ApiOperation(value = "3.1.8 重置app登录状态，清除redis缓存即可", notes = "状态码：18001:成功，18329：异常", response =
            ApiResponse.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)})
    @PutMapping("resetLoginState")
    public Object resetLoginState(@RequestParam String examCandidateId, @RequestParam Integer from) {
        try {
            if(from == 1){
                // app
                redisTemplate.delete(Constants.APP_LOGIN_ACCOUNT + examCandidateId);
            }
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (Exception e) {
            log.error("", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.KW_PHOTO_UPLOAD_BY_HAND_FAIL_CODE.getCode())
                    .build();
        }
    }

    @ApiOperationSort(9)
    @ApiOperation(value = "3.1.9 撤销交卷", notes = "状态码：18001:成功，18209：异常", response = ApiResponse.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateIds", value = "考生IDs", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)})
    @PutMapping("cancelSubmitPaper")
    public Object cancelSubmitPaper(@RequestBody CancelSubmitPaperDTO cancelSubmitPaperDTO) {
        try {
        	iCandidateProcessService.cancelSubmitPaper(cancelSubmitPaperDTO);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.KS_CANCEL_SUBMIT_PAPER_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_CANCEL_SUBMIT_PAPER_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperationSort(10)
    @ApiOperation(value = "3.1.10 设置考生跳过双监控验证", notes = "状态码：18001:成功，42031001：异常", response = ApiResponse.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)
    ,@ApiImplicitParam(name = "pwd", value = "密码", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)})
    @PutMapping("setPassDualMonitoring")
    public Object setPassDualMonitoring(@RequestParam String examCandidateId, @RequestParam String pwd) {
        try {
        	log.info("3.1.10 设置考生跳过双监控验证参数【{}】,【{}】", examCandidateId, pwd);
        	String passAppMonitoringPwd =  configManager.getPassAppMonitoringPwd();
        	if(!passAppMonitoringPwd.equals(pwd)) {
        		return ApiResponse.builder()
                        .code(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode())
                        .data("密码错误")
                        .build();
        	}
        	CandidateProcess candidateProcessUpdate = new CandidateProcess();
        	candidateProcessUpdate.setMobileLoginStatus(1);
        	QueryWrapper<CandidateProcess> query = Wrappers.query();        	
            query.lambda().eq(CandidateProcess::getCandidateId, examCandidateId)
            .eq(CandidateProcess::getDelFlag, false);
            iCandidateProcessService.update(candidateProcessUpdate, query);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data("设置成功")
                    .build();
        } catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperationSort(11)
    @ApiOperation(value = "3.1.11 获取大屏监控数据接口", notes = "状态码：18001:成功，42031101：异常", response = MonitorScreenVO.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)})
    @GetMapping("getMonitorScreen")
    public Object getMonitorScreen(@RequestParam String examId){
        try {
            log.info("3.1.11 获取大屏监控数数据接口的参数【{}】", examId);
            // sql有改动，process表有个字段is_started_exam表示考生是否进入过作答界面，如=1，则默认remainingTime值为1，否则为null
            List<CandidateTimeLength> candidateProcessList = iCandidateProcessService.getMonitorScreen(examId);
            List<MonitorScreenVO> monitorScreenList = candidateProcessList.stream()
            		.map(cpl -> new MonitorScreenVO(cpl.getCandidateId(), cpl.getRemainingTime(), cpl.getExamStatus()))
            		.collect(Collectors.toList());
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(monitorScreenList)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_MONITOR_SCREEN_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_MONITOR_SCREEN_ERROR.getCode())
                    .build();
        }
    }


    @ApiOperation(value = "3.1.13 统分(所有考生)")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试项目id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "mapJson", value = "小题标答和分数", dataType = DataType.STRING, paramType = ParamType.BODY, required = true)
    })
    @ApiOperationSort(13)
    @PutMapping("calculateAllStudentScores")
    public ApiResponse calculateAllStudentScores(@RequestParam String examId, @RequestBody String mapJson) {
        try {
            Map map = JSON.parseObject(mapJson, Map.class);
            List<CandidateProcess> candidateProcessList = iCandidateProcessService.lambdaQuery()
                    .select(CandidateProcess::getExamId, CandidateProcess::getCandidateId, CandidateProcess::getExamStatus)
                    .eq(CandidateProcess::getExamId, examId).eq(CandidateProcess::getDelFlag, false).list();
            candidateProcessList.parallelStream().forEach(candidateProcess -> {
                if(candidateProcess.getExamStatus() >= CandidateStatusCode.COMMIT.getCode().intValue()) {
                    String examCandidateRsp = kwClient.getExamCandidateById(candidateProcess.getCandidateId());
                    log.info("调用考务系统查询考试考生信息：{}", examCandidateRsp);
                    ApiResponse<ExamCandidate> response = ApiResponse.ConvertRet(examCandidateRsp, ExamCandidate.class);
                    if (!Objects.equals(BizStatusCode.KW_SUCCESS_OK.getCode(), response.getCode()) || Objects.isNull(response.getData())) {
                        throw new KsBizRunTimeException(BizStatusCode.END_WHOLE_EXAM_ERROR.getCode(),
                                "调用考务系统查询考试考生信息失败,考生id:" + candidateProcess.getCandidateId());
                    }
                    ExamCandidate ec = response.getData();
                    boolean isHistory = Objects.equals(0, ec.getIsBackup());
                    List<PaperDataVO> paperDataList = JSON.parseArray(mapJson, PaperDataVO.class);
                    iScoreService.calcScoreByCandidate(examId, candidateProcess.getCandidateId(), paperDataList, isHistory);
                }
            });
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
        } catch (Exception e) {
            log.error("", e);
            return ApiResponse.builder().code(BizStatusCode.CALC_SCORE_ERROR.getCode()).build();
        }
    }

    @ApiOperation(value = "3.1.14 初始化考生过程和答案数据", notes = "状态码：18001:成功，18215：异常", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "json", value = "json", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
    })
    @ApiOperationSort(14)
    @GetMapping("initCandidateData")
    public Object initCandidateData(@RequestParam String json) {
        log.info("初始化考生数据: {}", json);
        InitCandidateDataVO vo = JSON.parseObject(json, InitCandidateDataVO.class);
        iCandidateProcessService.initCandidateData(vo);
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
    }
    
    @ApiOperation(value = "3.1.15 查询考生与试卷关系列表", notes = "状态码：18001:成功，18217：异常", response = ApiResponse.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examId", value = "考试项目id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	,@ApiImplicitParam(name = "candidateId", value = "考生id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	})
    @ApiOperationSort(15)
    @GetMapping("getCandidatePaper")
    public Object getCandidatePaper(@RequestParam String examId, @RequestParam String candidateId) {
    	 try {
    		 LambdaQueryWrapper<CandidatePaper> query = Wrappers.lambdaQuery();
    		 query.eq(CandidatePaper::getExamId, examId)
                     .eq(CandidatePaper::getCandidateId, candidateId);
             return ApiResponse.builder()
                     .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                     .data(iCandidatePaperService.list(query))
                     .build();
         } catch (Exception e) {
             log.error(StringUtils.join(BizStatusCode.QUERY_CANDIDATE_PAPER_ERROR.getCode(), ": "), e);
             return ApiResponse.builder()
                     .code(BizStatusCode.QUERY_CANDIDATE_PAPER_ERROR.getCode())
                     .build();
         }
    }
    
    @ApiOperation(value = "3.1.16 获取测评报告、推送成绩报告给大易", notes = "状态码：18001:成功，42031601：异常", response = ApiResponse.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examId", value = "考试项目id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	,@ApiImplicitParam(name = "candidateId", value = "考生id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	,@ApiImplicitParam(name = "subjectiveScore", value = "主观题成绩", dataType = DataType.STRING, paramType = ParamType.QUERY, required = false)
    	})
    @ApiOperationSort(16)
    @GetMapping("getReportAndSendScoreDayi")
    public Object getReportAndSendScoreDayi(@RequestParam String examId, @RequestParam String candidateId, @RequestParam(required = false) String subjectiveScore) {
    	try {    		
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())   
                    .data(iThirdInterfaceService.getReportAndSendScoreDayi(examId, candidateId, StringUtils.isBlank(subjectiveScore) ? null : new BigDecimal(subjectiveScore)))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.17 获取考生剩余时长", notes = "状态码：18001:成功，42031601：异常", response = ApiResponse.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examId", value = "考试项目id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	,@ApiImplicitParam(name = "candidateId", value = "考生id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    	})
    @ApiOperationSort(17)
    @GetMapping("getCandidateUseTime")
    public Object getCandidateUseTime(@RequestParam String examId, @RequestParam String candidateId) {
        try {
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidatePaperService.getCandidateUseTime(examId, candidateId))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.17 获取考生所考试卷包的所有试卷剩余时长")
    @ApiImplicitParams({@ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)})
    @ApiOperationSort(17)
    @GetMapping("getCandidateAllPaperList")
    public ApiResponse<List<CancelCommitPaper>> getCandidateAllPaperList(@RequestParam String examId,
                                                                         @RequestParam String candidateId) {
        try {
            return ApiResponse.<List<CancelCommitPaper>>builder().code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateProcessService.getCandidateAllPaperList(examId, candidateId)).build();
        } catch (Exception e) {
            log.error("{}", e);
            return ApiResponse.<List<CancelCommitPaper>>builder()
                    .code(BizStatusCode.GET_CANDIDATE_ALL_PAPER_LIST_FAIL.getCode()).build();
        }
    }
    @ApiOperation(value = "3.1.18 撤销交卷(试卷包)")
    @ApiImplicitParams({@ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "ids", value = "考生试卷表id，逗号分隔", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)})
    @ApiOperationSort(18)
    @GetMapping("cancelPackageCommit")
    public ApiResponse cancelPackageCommit(@RequestParam String examId, @RequestParam String candidateId,
                                           @RequestParam String ids,@RequestParam Integer isBackup,
                                           @RequestParam Integer logBackup, @RequestParam String userId) {
        try {
            iCandidatePaperService.cancelPackageCommit(examId, candidateId, ids,isBackup,logBackup, userId);
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
        } catch (Exception e) {
            log.error("{}", e);
            return ApiResponse.builder().code(BizStatusCode.GET_CANDIDATE_ALL_PAPER_LIST_FAIL.getCode()).build();
        }
    }
    
    @ApiOperationSort(19)
    @ApiOperation(value = "3.1.19 设置大陆考生跳过人脸核身", notes = "状态码：18001:成功，42031001：异常", response = ApiResponse.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)})
    @PutMapping("setPassFaceVaildForDaluCandidate")
    public Object setPassFaceVaildForDaluCandidate(@RequestParam String examCandidateId) {
        try {
        	log.info("3.1.19 设置大陆考生跳过人脸核身参数【{}】", examCandidateId);
        	/*
        	String passAppMonitoringPwd =  configManager.getPassAppMonitoringPwd();
        	if(!passAppMonitoringPwd.equals(pwd)) {
        		return ApiResponse.builder()
                        .code(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode())
                        .data("密码错误")
                        .build();
        	}
        	*/
        	QueryWrapper<CandidateProcess> query = Wrappers.query();        	
            query.lambda().eq(CandidateProcess::getCandidateId, examCandidateId)
            .eq(CandidateProcess::getDelFlag, false);
            CandidateProcess candidateProcessUpdate = iCandidateProcessService.getOne(query);
            if (candidateProcessUpdate == null) {
            	return ApiResponse.builder()
                        .code(BizStatusCode.CANDIDATE_UN_LOGIN.getCode())
                        .build();
            } else if (candidateProcessUpdate.getExamStatus().intValue() > CandidateStatusCode.LOGIN.getCode().intValue()
            		|| (candidateProcessUpdate.getCheckedIdCard() && candidateProcessUpdate.getTakePicture())) {
            		return ApiResponse.builder()
                            .code(BizStatusCode.CANDIDATE_CAN_NOT_SET_PASS_FACE.getCode())
                            .build();                
            }
        	candidateProcessUpdate.setCheckedIdCard(true);
        	candidateProcessUpdate.setTakePicture(true);
            iCandidateProcessService.updateById(candidateProcessUpdate);

            return ApiResponse.builder()
                    .code(BizStatusCode.KW_SUCCESS_OK.getCode())
                    .data("设置成功")
                    .build();
        } catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.SET_PASS_DUAL_MONITORING_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.20 考生加时", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "json", value = "数据对象", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(20)
    @GetMapping("addExtraTime")
    public ApiResponse addExtraTime(@RequestParam String json) {
        try {
            iCandidateProcessService.addExtraTimeByBatch(json);
        } catch (Exception e) {
            log.error("# 加时异常：", e);
        }
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
    }
    
    @ApiOperation(value = "3.1.21 获取考生测评报告失败原因", notes = "状态码：18001:成功，18221：异常", response = ApiResponse.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "candidateId", value = "考生id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)})
    @ApiOperationSort(21)
    @GetMapping("getEvalReportFaildReason")
    public Object getEvalReportFaildReason(@RequestParam String candidateId) {
        try {
        	LambdaQueryWrapper<XyEvalLog> query = Wrappers.lambdaQuery();
   		 		query.eq(XyEvalLog::getCandidateId, candidateId)
   		 		.orderByDesc(XyEvalLog::getCreateDate);
   		 	List<XyEvalLog> xyEvalLogs = xyEvalLogMapper.selectList(query);
   		 	String reason = "无请求记录";
   		 	if(xyEvalLogs.size() > 0) {
   		 		reason = xyEvalLogs.get(0).getResultData();
   		 	}
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(reason)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_EVAL_REPORT_FAILD_REASON.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_EVAL_REPORT_FAILD_REASON.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.22 重置登录次数", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(22)
    @GetMapping("resetLoginCount")
    public Object resetLoginCount(@RequestParam String examId, @RequestParam String candidateId, @RequestParam String userId) {
        iCandidateProcessService.resetLoginCount(examId, candidateId, userId);
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
    }
    
    @ApiOperation(value = "3.1.23 获取考生单张试卷客观题得分", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "admissionNumber", value = "准考证号", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "paperId", value = "试卷id", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "paperVersion", value = "试卷版本号", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(23)
    @GetMapping("getPaperKgScore")
    public Object getPaperKgScore(@RequestParam String examId, @RequestParam String admissionNumber, 
    						 @RequestParam String paperId, @RequestParam String paperVersion) {
    	try {
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.data(iCandidatePaperService.getPaperKgScore(examId, admissionNumber, paperId, paperVersion))
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.GET_PAPER_KG_SCORE_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_PAPER_KG_SCORE_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.1.24 获取兴业银行成绩列表", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(24)
    @GetMapping("getXyScore")
    public Object getXyScore(@RequestParam String candidateId) {
    	try {
			log.info("3.1.24 获取兴业银行成绩列表请求参数内容：【{}】", candidateId); 
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.data(iCandidatePaperService.getXyScore(candidateId))
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.GET_XY_SCORE_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_XY_SCORE_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.1.25 获取兴业银行成绩列表", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(25)
    @GetMapping("getXyCandidateUseTime")
    public Object getXyCandidateUseTime(@RequestParam String candidateId) {
    	try {
			log.info("3.1.25 获取兴业银行成绩列表答题时间请求参数内容：【{}】", candidateId);
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.data(iCandidatePaperService.getXyCandidateUseTime(candidateId))
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.GET_XY_USE_TIME_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_XY_USE_TIME_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.1.26 获取考试答题时长统计", response = ApiResponse.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(26)
    @GetMapping("getExamAnswerTime")
    public Object getExamAnswerTime(@RequestParam String examId) {
    	try {
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.data(iCandidatePaperService.getExamAnswerTime(examId))
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.GET_EXAM_ANSWER_TIME_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_EXAM_ANSWER_TIME_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperationSort(27)
    @ApiOperation(value = "3.1.27 更新过程表", response = ApiResponse.class)
    @PutMapping("updateCandidateProcess")
    public Object updateCandidateProcess(@RequestBody String json){
        try {
            log.info("13.1.27 更新过程表参数【{}】", json);
            CandidateProcess param = JSON.parseObject(json, CandidateProcess.class);
            if(StringUtils.isBlank(param.getExamId())
                    || StringUtils.isBlank(param.getCandidateId())){
                return ApiResponse.builder().code(BizStatusCode.UPDATE_CANDIDATE_PROCESS_PARAM_WRONG.getCode()).build();
            }
            LambdaQueryWrapper<CandidateProcess> queryWrapper = Wrappers.<CandidateProcess>lambdaQuery()
                    .eq(CandidateProcess::getExamId, param.getExamId())
                    .eq(CandidateProcess::getCandidateId, param.getCandidateId());

            CandidateProcess entity = new CandidateProcess();
            if(null != param.getExamStatus()) {
                entity.setExamStatus(param.getExamStatus());
            }
            if(null != param.getOtherScore()) {
                entity.setOtherScore(param.getOtherScore());
            }
            if(null != param.getEndTime()) {
                entity.setEndTime(param.getEndTime());
            }
            iCandidateProcessService.update(entity, queryWrapper);
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
        } catch (Exception e) {
            log.error("更新过程表异常: ", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.ERROR_UPDATE_CANDIDATE_PROCESS.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.28 获取试卷初始化进度", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(28)
    @GetMapping("/getInitCandidateDataProcess")
    public Object getInitCandidateDataProcess(@RequestParam String examId){
        try {
            log.info("3.1.28 获取试卷初始化进度 考务调用考试获取试卷初始化进度请求参数内容：【{}】", examId);
            String doneKey = KeyDefineCommon.getInitCandidateProcessKey(examId);
            String countKey = KeyDefineCommon.getInitCandidateProcessCountKey(examId);
            Integer doneNum = NumberUtils.toInt(String.valueOf(redisTemplate.opsForValue().get(doneKey)));
            Integer countNum = NumberUtils.toInt(String.valueOf(redisTemplate.opsForValue().get(countKey)));
            redisTemplate.expire(doneKey,20, TimeUnit.MINUTES);
            redisTemplate.expire(countKey,20, TimeUnit.MINUTES);
            if(doneNum >= countNum){
                redisTemplate.delete(doneKey);
                redisTemplate.delete(countKey);
            }
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(doneNum).build();
        } catch (Exception e){
            log.error("获取考生试卷初始化进度异常: ", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.INIT_PAPER_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.29 获取试卷初始化数据清理进度", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(29)
    @GetMapping("/getInitCandidateClearProcess")
    public Object getInitCandidateClearProcess(@RequestParam String examId){
        try {
            log.info("3.1.29 获取试卷初始化数据清理进度 考务调用考试获取试卷初始化数据清理进度请求参数内容：【{}】", examId);
            String doneKey = KeyDefineCommon.getInitCandidateCLearProcess(examId);
            String countKey = KeyDefineCommon.getInitCandidateCLearProcessCount(examId);
            Integer doneNum = NumberUtils.toInt(String.valueOf(redisTemplate.opsForValue().get(doneKey)));
            Integer countNum = NumberUtils.toInt(String.valueOf(redisTemplate.opsForValue().get(countKey)),-1);
            log.info("=======本次调用清理进度，已处理数据为:{},总共需要处理的数据为:{}",doneNum,countNum);
            redisTemplate.expire(doneKey,2, TimeUnit.HOURS);
            redisTemplate.expire(countKey,2, TimeUnit.HOURS);
            if(doneNum >= countNum && countNum != -1){
                log.info("=======本次调用清理进度已【达成】，已处理数据为:{},总共需要处理的数据为:{}",doneNum,countNum);
                redisTemplate.delete(doneKey);
                redisTemplate.delete(countKey);
            }
            return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(InitCandidateClearDTO
                    .builder().total(countNum).done(doneNum).build()
            ).build();
        } catch (Exception e){
            log.error("获取考生试卷清理进度异常: ", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.INIT_PAPER_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.1.30 获取身份验证状态", response = ApiResponse.class)
    @ApiImplicitParams({
    	@ApiImplicitParam(name = "examCandidateId", value = "考生Id", dataType = DataType.STRING,
                paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(30)
    @GetMapping("getcandidateAuthenticationStatus")
    public Object getcandidateAuthenticationStatus(@RequestParam String examCandidateId) {
    	try {
    		log.info("3.1.30 获取身份验证状态 考务调用考试获取身份验证状态请求参数内容：【{}】", examCandidateId);
    		LambdaQueryWrapper<CandidateProcess> queryCandidateProcess = Wrappers.lambdaQuery();
        	queryCandidateProcess.eq(CandidateProcess::getCandidateId, examCandidateId);
        	CandidateProcess candidateProcess = iCandidateProcessService.getOne(queryCandidateProcess);
        	Integer authenticationStatus = 0;
        	if (candidateProcess != null) {
        		authenticationStatus = candidateProcess.getCheckedIdCard() ? 1 : 0;
        	}
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.data(authenticationStatus)
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_VAILD_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_VAILD_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.31 获取考生加时信息", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(31)
    @GetMapping("getCandidateAddTime")
    public ApiResponse getCandidateAddTime(@RequestParam String examId, @RequestParam String candidateId) {
        List<CandidatePaper> data = iCandidatePaperService.list(Wrappers.<CandidatePaper>lambdaQuery()
                .eq(CandidatePaper::getExamId, examId).eq(CandidatePaper::getCandidateId, candidateId)
                .orderByAsc(CandidatePaper::getSort));
        List<PaperAddTimeDTO> list = new ArrayList<>();
        for (CandidatePaper datum : data) {
            // 未开考
            long timeLength = iCandidateProcessService.getRemainTime(datum);
            list.add(PaperAddTimeDTO.builder().paperId(datum.getPaperId()).paperVersion(datum.getPaperVersion())
                    .timeLength(datum.getExtraTime()/60/1000).remainingTime(timeLength).build());
        }
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(list).build();
    }

    @ApiOperation(value = "3.1.32 获取考生总加时时长", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(32)
    @GetMapping("getTotalExtraTime")
    public ApiResponse<Long> getTotalExtraTime(@RequestParam String examId, @RequestParam String candidateId) {
        long time = 0;
        List<CandidatePaper> data = iCandidatePaperService.lambdaQuery().select(CandidatePaper::getExtraTime)
                .eq(CandidatePaper::getExamId, examId).eq(CandidatePaper::getCandidateId, candidateId).list();
        for (CandidatePaper e : data) {
            if(e.getExtraTime() != null) {
                time += e.getExtraTime() / 60 / 1000;
            }
        }
        return ApiResponse.<Long>builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(time).build();
    }
    
    @ApiOperation(value = "3.1.33 推送考生成绩到成绩系统", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(33)
    @GetMapping("pushCandidateScoreToCj")
    public Object pushCandidateScoreToCj(@RequestParam String examId, @RequestParam(required = false) String candidateId) {
    	try {
    		log.info("3.1.33  推送考生成绩到成绩系统求参数内容：【{},{}】", examId, candidateId);
    		if(StringUtils.isBlank(candidateId)) {
                List<CandidateProcess> list = iCandidateProcessService.lambdaQuery()
                        .select(CandidateProcess::getCandidateId).eq(CandidateProcess::getExamId, examId).list();
                for (CandidateProcess candidateProcess : list) {
                    iThirdInterfaceService.pushScoreToCj(examId, candidateProcess.getCandidateId(), null, null);
                }
            } else {
                iThirdInterfaceService.pushScoreToCj(examId, candidateId, null, null);
            }
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.PUSH_SCORE_TO_CJ_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.PUSH_SCORE_TO_CJ_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.1.34 推送考生成绩到成绩系统", response = ApiResponse.class)
    @ApiOperationSort(34)
    @PutMapping("pushCandidateScoreToCjKw")
    public Object pushCandidateScoreToCjKw(@RequestBody PushCandidateScoreToCjVO pushCandidateScoreToCj) {
    	try {
    		log.info("3.1.34  推送考生成绩到成绩系统求参数内容：【{},{}】", pushCandidateScoreToCj.toString());
    		iThirdInterfaceService.pushScoreToCj(pushCandidateScoreToCj.getExamId(), pushCandidateScoreToCj.getCandidateId(), pushCandidateScoreToCj.getCandidateSubjectiveScoreList(), null);
    		return ApiResponse.builder().
        		code(BizStatusCode.KS_SUCCESS_OK.getCode())
        		.build();
    	} catch (Exception e) {
        	log.error(StringUtils.join(BizStatusCode.PUSH_SCORE_TO_CJ_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.PUSH_SCORE_TO_CJ_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.1.35 清理考生试卷信息", response = ApiResponse.class)
    @ApiOperationSort(35)
    @PostMapping("clearCandidatePaperData")
    public Object clearCandidatePaperData(@RequestBody List<InitCandidateDataVO> datas){
        try {
            log.info("3.1.35  清理考生试卷信息参数内容：【{}】",datas.size());
            initCandidateDataService.clearCandidateData(datas);
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(Boolean.TRUE)
                    .build();
        } catch (Exception e){
            log.error("清理考生试卷信息失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.INIT_PAPER_ERROR.getCode())
                    .data(Boolean.FALSE)
                    .build();
        }
    }
 	@ApiOperation(value = "3.36 获取考生客观题成绩", response = ApiResponse.class)
    @ApiOperationSort(36)
    @PostMapping("getCandiadatePapers")
    public Object getCandiadatePapers(@RequestBody CandidateKgScoreDTO requestParam){
        try {
            log.info("3.36 获取考生客观题成绩参数内容：【{}】",requestParam.toString());
            LambdaQueryWrapper<CandidatePaper> query = Wrappers.lambdaQuery();
            query.eq(CandidatePaper::getExamId, requestParam.getExamId());
            if(!CollectionUtils.isEmpty(requestParam.getCandidateIdList())){
                query.in(CandidatePaper::getCandidateId, requestParam.getCandidateIdList());
            }
            List<CandidatePaper> data = iCandidatePaperService.list(query);
            
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(data)
                    .build();
        } catch (Exception e){
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_PAPERS_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_PAPERS_ERROR.getCode())
                    .build();
        }
    }
    
    @ApiOperation(value = "3.37 获取考生科目试卷信息", response = ApiResponse.class)
    @ApiOperationSort(37)
    @GetMapping("getCandidateSubjectPaperInfo")
    public Object getCandidateSubjectPaperInfo(@RequestParam String examId,  @RequestParam(required = false) String admissionNumber){
        try {
            log.info("3.37 获取考生科目试卷信息参数内容：【{},{}】", examId, admissionNumber);           
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidatePaperService.getCandidateSubjectPaperInfo(examId, admissionNumber))
                    .build();
        } catch (Exception e){
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_SUBJECT_PAPER_INFO_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_SUBJECT_PAPER_INFO_ERROR.getCode())
                    .build();
        }
    }

	@ApiOperation(value = "3.38 获取考生试卷客观题成绩", response = ApiResponse.class)
    @ApiOperationSort(38)
    @GetMapping("getKgScoreForPaper")
    public Object getKgScoreForPaper(String examId, String candidateId, String paperId, String paperVersion, Integer isBackup){
        try {
            log.info("3.38 获取考生试卷客观题成绩参数内容：【{},{},{},{}】", examId, candidateId, paperId, paperVersion);
            LambdaQueryWrapper<CandidatePaper> queryCandidatePaper = Wrappers.lambdaQuery();
            queryCandidatePaper.select(CandidatePaper::getObjectiveScore, CandidatePaper::getAdmissionNumber)
                    .eq(CandidatePaper::getExamId, examId)
                .eq(CandidatePaper::getCandidateId, candidateId)
                .eq(CandidatePaper::getPaperId, paperId)
                .eq(CandidatePaper::getPaperVersion, paperVersion);
            CandidatePaper candidatePaper = iCandidatePaperService.getOne(queryCandidatePaper);
            ObjectiveScoreForPaperDTO objectiveScoreForPaper = new ObjectiveScoreForPaperDTO();
            objectiveScoreForPaper.setExamId(examId);
            objectiveScoreForPaper.setPaperId(paperId);
            objectiveScoreForPaper.setPaperVersion(paperVersion);
            BigDecimal kgScore = BigDecimal.ZERO;
            if (candidatePaper != null && candidatePaper.getObjectiveScore() != null) {
                objectiveScoreForPaper.setAdmissionNumber(candidatePaper.getAdmissionNumber());
                kgScore = candidatePaper.getObjectiveScore();
            }
            objectiveScoreForPaper.setKgScore(kgScore);
            List<ObjectiveItemScoreDTO> objectiveItemScoreList = new ArrayList<ObjectiveItemScoreDTO>();
            if(Objects.equals(0,isBackup)){
                objectiveItemScoreList = iCandidateAnswerService.getObjectiveItemScore(examId, candidateId, paperId,  paperVersion);
            } else {
                objectiveItemScoreList = iCandidateAnswerBackupService.getObjectiveItemScore(examId, candidateId, paperId, paperVersion);
            }
            objectiveScoreForPaper.setObjectiveItemScoreList(objectiveItemScoreList);
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(objectiveScoreForPaper)
                    .build();
        } catch (Exception e){
            log.error("获取考生试卷客观题成绩失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_KG_SCORE_FOR_PAPER_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.38 获取考生日志操作时间", response = ApiResponse.class)
    @ApiOperationSort(38)
    @GetMapping("getCandidateLogBeginTime")
    public Object getCandidateLogBeginTime(@RequestParam String examId, @RequestParam String operatorType){
        try {
            log.info("3.38 获取考生日志操作时间：【{}，{}】",examId, operatorType);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateLogOtherService.getCandidateLogBeginOtherTime(examId, operatorType))
                    .build();
        } catch (Exception e){
            log.error("获取考生日志操作时间失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_LOG_BEGIN_TIME_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.39 验证是否可以导出成绩", response = ApiResponse.class)
    @ApiOperationSort(39)
    @GetMapping("vaildDownLoadScore")
    public Object vaildDownLoadScore(@RequestParam String examId){
        try {
            log.info("3.39 验证是否可以导出成绩请求参数：【{}】",examId);

            LambdaQueryWrapper<CandidateProcess> queryCandidateProcess = Wrappers.lambdaQuery();
            queryCandidateProcess.select(CandidateProcess::getExamId)
                    .eq(CandidateProcess::getExamId, examId)
                    .and(c -> {
                        c.eq(CandidateProcess::getExamStatus, CandidateStatusCode.LOGIN.getCode().intValue());
                        c.or().eq(CandidateProcess::getExamStatus, CandidateStatusCode.TESTING.getCode().intValue());
                        c.or().eq(CandidateProcess::getExamStatus, CandidateStatusCode.INTERRUPT.getCode().intValue());
                    });
            Integer candidateProcessCount = iCandidateProcessService.count(queryCandidateProcess);
            if(candidateProcessCount.intValue() > 0){
                return ApiResponse.builder()
                        .code(BizStatusCode.HAS_UNCOMPLETE_CANDIDATE.getCode())
                        .data("存在未完成考试的考生，请先”结束考试“或强制结束已登录、考试中、中断的考生")
                        .build();
            }
            Integer unStatisticsScoreCount = candidateProcessMapper.unStatisticsScore(examId);
            Integer errorScoreCount = candidateProcessMapper.errorScore(examId);
            Integer nullAnswerHasScoreCount = candidateProcessMapper.nullAnswerHasScore(examId);
            Integer correctAnswerHasNotScoreCount = candidateProcessMapper.correctAnswerHasNotScore(examId);
            Integer errorAnswerHasScoreCount = candidateProcessMapper.errorAnswerHasScore(examId);

            if(unStatisticsScoreCount.intValue() > 0 || errorScoreCount.intValue() > 0
                    || nullAnswerHasScoreCount.intValue() > 0 || correctAnswerHasNotScoreCount.intValue() > 0
                    || errorAnswerHasScoreCount.intValue() > 0) {
                return ApiResponse.builder()
                        .code(BizStatusCode.HAS_NOT_VAIID_SCORE.getCode())
                        .data("请校验数据后再导出")
                        .build();
            }


            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (Exception e){
            log.error("验证是否可以导出成绩失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.VAILD_DOWNLOAD_SCORE_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.41 设置考生跳过照片比对", response = ApiResponse.class)
    @ApiOperationSort(41)
    @GetMapping("setPassCandidatePhotoVerification")
    public Object setPassCandidatePhotoVerification(@RequestParam String examId, @RequestParam String candidateId) {
        try {
            log.info("3.41 设置考生跳过照片比对：【{}，{}】", examId, candidateId);

            iCandidateProcessService.setPassCandidatePhotoVerification(examId, candidateId);
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (Exception e) {
            log.error("设置考生跳过照片比对失败", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.SET_PASS_CANDIDATE_PHOTO_ERROR.getCode()).build();
        }
    }

    @ApiOperation(value = "3.40 设置跳过考试步骤", response = ApiResponse.class)
    @ApiOperationSort(40)
    @GetMapping("setCandidatePassSteps")
    public Object setCandidatePassSteps(@RequestParam String examId, @RequestParam String candidateId, @RequestParam String stepName,@RequestParam  String userId,@RequestParam  String userName){
        try {
            log.info("3.40 设置跳过考试步骤参数：【{}，{}，{}】",examId, candidateId,stepName);

            CandidateProcess candidateProcess = iCandidateProcessService.lambdaQuery()
                    .eq(CandidateProcess::getCandidateId, candidateId)
                    .eq(CandidateProcess::getExamId,examId)
                    .eq(CandidateProcess::getDelFlag, false)
                    .last("LIMIT 1")
                    .one();

            if (candidateProcess == null) {
                // 未登录，不允许操作
                return ApiResponse.builder()
                        .code(BizStatusCode.PASS_STEP_SET_NO_LOGIN.getCode())
                        .build();
            } else if (candidateProcess.getExamStatus().intValue() >= CandidateStatusCode.COMMIT.getCode().intValue()) {
                // 考试结束状态，不允许操作
                return ApiResponse.builder()
                        .code(BizStatusCode.PASS_STEP_SET_OVER_EXAM.getCode())
                        .build();
            }

            //2. 入库
            iCandidatePassService.setCandidatePassSteps(examId, candidateId, stepName, userId, userName);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (KsException i){
            log.error("3.40 设置跳过考试步骤异常",i);
            return ApiResponse.builder()
                    .code(i.getErrCode())
                    .build();
        } catch (Exception e){
            log.error("3.40 设置跳过考试步骤异常",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.PASS_STEP_SET_FAIL.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.42 异常管理审核分页查询", response = ApiResponse.class)
    @ApiOperationSort(42)
    @PostMapping("/pageApplyCheck")
    public Object pageApplyCheck(@RequestBody PageApplyCheckDTO pageApplyCheckDTO) {
        try {
            log.info("3.42 异常管理审核分页查询：【{}】", pageApplyCheckDTO);
            Page<ApplyCheck> applyCheckPage = iApplyCheckService.pageApplyCheck(pageApplyCheckDTO);
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(JSON.toJSONString(applyCheckPage))
                    .build();
        } catch (Exception e) {
            log.error("异常管理审核分页查询报错：", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(JSON.toJSONString(pageApplyCheckDTO)).build();
        }
    }

    @ApiOperation(value = "3.41 获取考生跳过步骤操作时间", response = ApiResponse.class)
    @ApiOperationSort(38)
    @GetMapping("getCandidatePassStepsOptionDate")
    public Object getCandidatePassStepsOptionDate(@RequestParam String examId, @RequestParam String passStep){
        try {
            log.info("3.41 获取考生跳过步骤操作时间：【{}，{}】",examId, passStep);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidatePassService.getCandidatePassStepsByExamId(examId, passStep))
                    .build();
        } catch (Exception e){
            log.error("3.41 获取考生跳过步骤操作时间异常",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_PASS_STEP_SET_DATE_FAIL.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.43 异常管理审核", response = ApiResponse.class)
    @ApiOperationSort(43)
    @PutMapping("/applyCheck")
    public Object applyCheck(@RequestBody ApplyCheckDTO applyCheck) {
        try {
            log.info("3.43 异常管理审核：【{}】", applyCheck);
            ApplyCheck nextApplyCheck = iApplyCheckService.applyCheck(applyCheck);
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(nextApplyCheck)
                    .build();
        } catch (Exception e){
            log.error("异常管理审核报错：",e);
            return ApiResponse.builder()
                    .code(CANDIDATE_APPLY_CHECK_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.44 根据考生id获取考生科目试卷信息", response = ApiResponse.class)
    @ApiOperationSort(44)
    @GetMapping("findCandidateSubjectPaperInfoList")
    public Object findCandidateSubjectPaperInfoList(@RequestParam String examId,  @RequestParam List<String> candidateIds){
        try {
            log.info("3.44 根据考生id获取考生科目试卷信息内容：【{},{}】", examId, JSONObject.toJSONString(candidateIds));
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidatePaperService.findCandidateSubjectPaperInfoList(examId, candidateIds))
                    .build();
        } catch (Exception e){
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_SUBJECT_PAPER_INFO_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_SUBJECT_PAPER_INFO_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.45 异常管理审核根据异常类型和考试查询下一条待审核", response = ApiResponse.class)
    @ApiOperationSort(45)
    @PostMapping("/nextApplyCheck")
    public ApiResponse<ApplyCheck> nextApplyCheck(@RequestBody ApplyCheckDTO applyCheck) {
        ApiResponse.ApiResponseBuilder<ApplyCheck> builder = ApiResponse.builder();
        try {
            log.info("3.45 异常管理审核：【{}】", applyCheck);
            ApplyCheck nextApplyCheck = iApplyCheckService.nextApplyCheck(applyCheck);
            return builder.
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(nextApplyCheck)
                    .build();
        } catch (Exception e){
            log.error("异常管理审核报错：",e);
            return builder
                    .code(BizStatusCode.KW_API_FIND_APPLY_CHECK_PASS_LIST_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.46 异常管理审核考生设备情况", response = ApiResponse.class)
    @ApiOperationSort(46)
    @GetMapping("/applyCheckDeviceInfo")
    public ApiResponse<DeviceVO> applyCheckDeviceInfo(@RequestParam String applyCheckId) {
        ApiResponse.ApiResponseBuilder<DeviceVO> builder = ApiResponse.builder();
        try {
            log.info("3.46 异常管理审核考生设备情况：【{}】", applyCheckId);
            return builder.
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iApplyCheckService.applyCheckDeviceInfo(applyCheckId))
                    .build();
        } catch (Exception e){
            log.error("异常管理审核考生设备情况报错：", e);
            return builder
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(null)
                    .build();
        }
    }

    @ApiOperation(value = "3.47 获取考生日志交卷时间", response = ApiResponse.class)
    @ApiOperationSort(47)
    @GetMapping("getCandidateLogAnswerCommitTime")
    public Object getCandidateLogAnswerCommitTime(@RequestParam String examId, @RequestParam String operatorType, @RequestParam String finishType){
        try {
            log.info("3.47 获取考生日志交卷时间：【{}，{}，{}】",examId, operatorType, finishType);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateLogAnswerService.getCandidateLogCommitTime(examId, operatorType, finishType))
                    .build();
        } catch (Exception e){
            log.error("获取考生日志交卷时间",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_LOG_BEGIN_TIME_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.48 主动发送审核结果消息给学生")
    @ApiOperationSort(48)
    @PutMapping("/sendApplyCheckResult2Student")
    public ApiResponse<Boolean> sendApplyCheckResult2Student(@RequestParam String logOtherId) {
        try {
            log.info("3.48 主动发送审核结果消息给学生：【{}】", logOtherId);
            iCandidateLogOtherService.sendApplyCheckResult2StudentByLogOtherId(logOtherId);
            return ApiResponse.<Boolean>builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(true)
                    .build();
        } catch (KsException e){
            log.error("主动发送审核结果消息给学生报错：",e);
            return ApiResponse.<Boolean>builder()
                    .code(e.getErrCode())
                    .data(false)
                    .build();
        } catch (Exception e){
            log.error("主动发送审核结果消息给学生报错：",e);
            return ApiResponse.<Boolean>builder()
                    .code(BizStatusCode.CANDIDATE_OTHER_LOG_SEND_STUDENT_MSG_ERROR.getCode())
                    .data(false)
                    .build();
        }
    }

    @ApiOperation(value = "3.49 异常管理审核根据id查询待审核", response = ApiResponse.class)
    @ApiOperationSort(49)
    @GetMapping("/getApplyCheck")
    public ApiResponse<ApplyCheck> getApplyCheck(@RequestParam String applyCheckId) {
        ApiResponse.ApiResponseBuilder<ApplyCheck> builder = ApiResponse.builder();
        try {
            log.info("3.49 异常管理审核：【{}】", applyCheckId);
            return builder.
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iApplyCheckService.getById(applyCheckId))
                    .build();
        } catch (Exception e){
            log.error("异常管理审核报错：",e);
            return builder
                    .code(BizStatusCode.KW_API_GET_APPLY_CHECK_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.50 根据考试id查询审核通过的考生id列表", response = ApiResponse.class)
    @ApiOperationSort(50)
    @GetMapping("/findApplyCheckCandidateIdAuditPassList")
    public ApiResponse<List<String>> findApplyCheckCandidateIdAuditPassList(@RequestParam String examId, @RequestParam String candidateIds) {
        ApiResponse.ApiResponseBuilder<List<String>> builder = ApiResponse.builder();
        try {
            return builder.
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iApplyCheckService.findApplyCheckCandidateIdAuditPassList(examId, candidateIds))
                    .build();
        } catch (Exception e){
            log.error("根据考试id查询审核通过的考生id列表报错：",e);
            return builder
                    .code(BizStatusCode.FIND_APPLY_CHECK_CANDIDATE_ID_AUDIT_ERROR.getCode())
                    .build();
        }
    }


    @ApiOperation(value = "3.51 数据核查", response = ApiResponse.class)
    @ApiOperationSort(51)
    @GetMapping("dataCheck")
    public Object dataCheck(@RequestParam String examId){
        try {
            log.info("3.51 数据核查请求参数：【{}】",examId);
            ExecutorFactory.getThreadPoolExecutor().execute(() -> iCandidateProcessService.dataCheck(examId));
            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        } catch (Exception e) {
            log.error("数据核查失败", e);
            return ApiResponse.builder()
                    .code(BizStatusCode.DATA_CHECK_ERROR.getCode());
        }
    }

    @ApiOperation(value = "3.52 根据获取轨迹最大时间", response = ApiResponse.class)
    @ApiOperationSort(52)
    @GetMapping("/getMaxTimeByOperateType")
    public ApiResponse<List<CandidateLogVo>> getMaxTimeByOperateType(@RequestParam String examId, @RequestParam String operateType) {
        try {
            return ApiResponse.<List<CandidateLogVo>>builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateLogOtherService.getMaxTimeByOperateType(examId,operateType))
                    .build();
        } catch (Exception e){
            log.error("根据考试id获取轨迹最大时间报错：",e);
            return ApiResponse.<List<CandidateLogVo>>builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.53 根据登录次数大于等于3的考生", response = ApiResponse.class)
    @ApiOperationSort(53)
    @GetMapping("/getMaxAndMinTimeByOperateType")
    public ApiResponse<List<CandidateLogVo>> getMaxAndMinTimeByOperateType(@RequestParam String examId, @RequestParam String operateType) {
        try {
            return ApiResponse.<List<CandidateLogVo>>builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateLogOtherService.getMaxAndMinTimeByOperateType(examId,operateType))
                    .build();
        } catch (Exception e){
            log.error("根据考试id获取轨迹最大时间报错：",e);
            return ApiResponse.<List<CandidateLogVo>>builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.54 获取考生人脸核身审核不通过时间", response = ApiResponse.class)
    @ApiOperationSort(54)
    @GetMapping("getApplyCheckUnPassFaceTime")
    public Object getApplyCheckUnPassFaceTime(@RequestParam String examId){
        try {
            log.info("3.51 获取考生人脸核身审核不通过时间请求参数：【{}】",examId);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iApplyCheckService.getApplyCheckUnPassFaceTime(examId))
                    .build();
        } catch (Exception e){
            log.error("获取考生人脸核身审核不通过时间失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_APPLY_CHECK_UN_PASS_FACE_TIME_ERROR.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.55 获取撤销交卷时间", response = ApiResponse.class)
    @ApiOperationSort(55)
    @GetMapping("getCancelCommitTime")
    public Object getCancelCommitTime(@RequestParam String examId){
        try {
            log.info("3.51 获取撤销交卷时间请求参数：【{}】",examId);

            return ApiResponse.builder().
                    code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidateLogOtherService.getCandidateLogBeginOtherTime(examId, "403"))
                    .build();
        } catch (Exception e){
            log.error("获取撤销交卷时间失败",e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANCEL_COMMIT_TIME.getCode())
                    .build();
        }
    }

    @ApiOperationSort(56)
    @ApiOperation(value = "3.56 获取考生答案（批量）", notes = "状态码：18001:成功，42030601：异常", response = CandidateAnswer.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
            paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "examCandidateStr", value = "考生信息", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "isObjective", value = "是否主客观，true客观，null表示查全部", dataType = DataType.STRING,
                    paramType = ParamType.QUERY)})
    @GetMapping("getCandidateAnswerByCandidateList")
    public Object getCandidateAnswerByCandidateIds(@RequestParam String examId, @RequestParam String examCandidateStr, @RequestParam(required = false) Boolean isObjective) {
        try {
            LambdaQueryWrapper<CandidateProcess> queryCandidateProcess = Wrappers.lambdaQuery();
            List<ExamCandidate> examCandidateList = JSON.parseArray(examCandidateStr, ExamCandidate.class);
            Map<String, Integer> candidateBackupMap = examCandidateList.stream().collect(Collectors.toMap(vo -> vo.getId(), vo -> vo.getIsBackup()));
            queryCandidateProcess.eq(CandidateProcess::getExamId, examId).in(CandidateProcess::getCandidateId, candidateBackupMap.keySet().stream().collect(Collectors.toList()));
            List<CandidateProcess> candidateProcessList = iCandidateProcessService.list(queryCandidateProcess);
            List<CandidateAnswerSimpleVO> candidateAnswerList = new ArrayList<>();
            if (!candidateProcessList.isEmpty()) {
                List<String> noBackupCandidateIds = candidateProcessList.stream()
                        .filter(vo -> Objects.equals(0, candidateBackupMap.get(vo.getCandidateId())))
                        .map(vo -> vo.getCandidateId()).collect(Collectors.toList());
                Set<Integer> noBackupMaxScoreVers = candidateProcessList.stream()
                        .filter(vo -> Objects.equals(0, candidateBackupMap.get(vo.getCandidateId())))
                        .map(vo -> vo.getMaxScoreVer()).collect(Collectors.toSet());
                if(!noBackupMaxScoreVers.isEmpty() && !noBackupMaxScoreVers.isEmpty()) {
                    List<CandidateAnswerSimpleVO> notBackupList = iCandidateAnswerService.selectSimpleAnswerList(examId, noBackupCandidateIds, noBackupMaxScoreVers.stream().collect(Collectors.toList()));
                    if (!notBackupList.isEmpty()) {
                        candidateAnswerList.addAll(notBackupList);
                    }
                }
                List<String> backupCandidateIds = candidateProcessList.stream()
                        .filter(vo -> !Objects.equals(0, candidateBackupMap.get(vo.getCandidateId())))
                        .map(vo -> vo.getCandidateId()).collect(Collectors.toList());
                Set<Integer> backupMaxScoreVers = candidateProcessList.stream()
                        .filter(vo -> !Objects.equals(0, candidateBackupMap.get(vo.getCandidateId())))
                        .map(vo -> vo.getMaxScoreVer()).collect(Collectors.toSet());
                if(!backupCandidateIds.isEmpty() && !backupMaxScoreVers.isEmpty()) {
                    List<CandidateAnswerSimpleVO> backupList = iCandidateAnswerBackupService.selectSimpleAnswerBackupList(examId, backupCandidateIds, backupMaxScoreVers.stream().collect(Collectors.toList()));
                    if (!backupList.isEmpty()) {
                        candidateAnswerList.addAll(backupList);
                    }
                }
            }
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(candidateAnswerList)
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_CANDIDATE_ANSWER_FAIL.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_CANDIDATE_ANSWER_FAIL.getCode())
                    .build();
        }
    }

    @ApiOperation(value = "3.57 获取考生剩余时长（批量）", notes = "状态码：18001:成功，42031601：异常", response = ApiResponse.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试项目id", dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
            ,@ApiImplicitParam(name = "candidateIds", value = "考生id", dataType = DataType.ARRAY, paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(57)
    @GetMapping("getCandidateUseTimeByCandidateIds")
    public Object getCandidateUseTimeByCandidateIds(@RequestParam String examId, @RequestParam List<String> candidateIds) {
        try {
            return ApiResponse.builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(iCandidatePaperService.getCandidateUseTimeByCandidateIds(examId, candidateIds))
                    .build();
        } catch (Exception e) {
            log.error(StringUtils.join(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode(), ": "), e);
            return ApiResponse.builder()
                    .code(BizStatusCode.GET_XY_REPORT_AND_SEND_DAYI_ERROR.getCode())
                    .build();
        }
    }


}
